﻿using OSGeo.OGR;
using OSGeo.OSR;
using shp2postgres.gdal;
using shp2postgres.log;
using shp2postgres.sql;
using shpTools;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Threading;
using static shp2postgres.log.Log;

namespace shp2postgres
{
    public partial class Form1 : Form
    {
        SQLTools sqlTools = new SQLTools();

        // shp数据空间集合
        private List<Geometry> geometries;

        // shp数据属性集合
        private List<Dictionary<String, Object>> properties;

        // 存放shp文件字段属性信息
        private List<FieldModel> fieldModels = new List<FieldModel>();

        private SpatialReference spatialReference;

        private wkbGeometryType wkbGeometryType;

        private string shpFile;

        public string epsg { get; set; }
        private GdalTools gdalTools;

        public Form1()
        {
            InitializeComponent();
            GdalTools.InitGDAL();
            // 忽略对跨线程调用的检测
            CheckForIllegalCrossThreadCalls = false;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Log.setDelegate(new Log.MessageToTextBox(SetText));
        }

        /// <summary>
        /// 打开连接PostgreSQL数据库的窗口
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void postgresqlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Log.InfoAsync("连接PostgreSQL数据库");
            SQLForm sqlForm = new SQLForm(DabaBaseType.PostgreSQL, sqlTools);
            sqlForm.FormClosed += SqlForm_FormClosed;
            sqlForm.ShowDialog();
        }


        private void sqlserverToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SQLForm sqlForm = new SQLForm(DabaBaseType.SQLServer, sqlTools);
            sqlForm.FormClosed += SqlForm_FormClosed;
            sqlForm.ShowDialog();
        }

        private void SqlForm_FormClosed(object sender, FormClosedEventArgs e)
        {
            SQLForm sqlForm = (SQLForm)sender;
            sqlForm.FormClosed -= SqlForm_FormClosed;
            if (sqlForm.connectioned)
            {
                switch (sqlForm.DBType)
                {
                    case DabaBaseType.SQLServer:
                        postgresToolStripMenuItem.Checked = false;
                        sqlserverToolStripMenuItem.Checked = true;
                        break;
                    case DabaBaseType.PostgreSQL:
                        postgresToolStripMenuItem.Checked = true;
                        sqlserverToolStripMenuItem.Checked = false;
                        break;
                    default:
                        break;
                }
                
                btn_check_table.Enabled = true;
                //btn_import.Enabled = true;
            }
            sqlForm.Dispose();
        }


        private void open_shp_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.Filter = "*.shp|";
            
            openFileDialog.FileOk += (f, c) => {
                if (c.Cancel)
                {
                    openFileDialog.Dispose();
                    return;
                } 
                
                if (!openFileDialog.FileName.EndsWith(".shp"))
                {
                    MessageBox.Show("请选择shp文件！");
                    return;
                }
                gdalTools = new GdalTools();

                this.shpFile = openFileDialog.FileName;
                // 检查是否有.cpg文件
                string cpgf = shpFile.Replace(".shp", ".cpg");
                if (File.Exists(cpgf))
                {
                    string[] fls = File.ReadAllLines(cpgf);
                    foreach(string ln in fls)
                    {
                        string str = ln.Trim();
                        if (str.Length > 0)
                        {
                            if ("UTF-8".Equals(str))
                            {
                                gdalTools.Encoding = Encoding.UTF8;
                            }
                        }
                    }
                }
                
                (SpatialReference siref, FeatureDefn fields, List<Dictionary<String, Object>> dataList, List<Geometry> geoList) = gdalTools.readShp(openFileDialog.FileName);
                this.geometries = geoList;
                this.properties = dataList;
                this.spatialReference = siref;
                this.wkbGeometryType = fields.GetGeomType();
                fieldModels.Clear();
                this.dataGridViewFields.DataSource = null;

                Log.InfoAsync($"已打开shp格式文件：{openFileDialog.FileName}，\t 总数据量：{properties.Count} 条");
                trackBar.Maximum = properties.Count;
                trackBar.Value = 1;
                textBox_total.Text = properties.Count.ToString();

                // 组织列表内容
                DataTable dataTable = new DataTable();
                int count = fields.GetFieldCount();
                for (int i = 0; i < count; i++)
                {
                    FieldDefn fieldDefn = fields.GetFieldDefn(i);
                    if (fieldDefn == null) continue;
                    FieldModel fieldModel = new  FieldModel();
                    fieldModel.GdalIndex = i;
                    string fn = GdalTools.GetFieldName(fieldDefn, gdalTools.Encoding);
                    //string fn = fieldDefn.GetName();
                    fieldModel.GdalField = fn;
                    fieldModel.GdalFieldType = fieldDefn.GetFieldType();
                    fieldModel.SqlField = fn;
                    fieldModel.SqlFieldType = FieldModel.GetSQLFieldType(fieldModel.GdalFieldType);
                    fieldModels.Add(fieldModel);

                    DataColumn dataColumn = new DataColumn(fieldModel.SqlField);
                    dataTable.Columns.Add(dataColumn);
                }
                
                FieldModel fieldModelGeom = new FieldModel();
                fieldModelGeom.SqlField = "the_geom";
                fieldModelGeom.IsGeom = true;
                fieldModelGeom.SqlFieldType = geoList[0].GetGeometryType().ToString();
                fieldModels.Add(fieldModelGeom);

                this.dataGridViewFields.DataSource = fieldModels;
                this.dataGridViewFields.ColumnAdded += DataGridViewFields_ColumnAdded;
                this.dataGridViewFields.RowPostPaint += DataGridViewFields_RowPostPaint;
                this.dataGridView_shp.RowPostPaint += DataGridView_shp_RowPostPaint; ;
                
                if (null != siref)
                {
                    epsg = siref.GetAttrValue("AUTHORITY", 1);
                    this.textBox_epsg.Text = epsg;
                    siref.ExportToWkt(out string referenceWkt);
                    this.textBoxSrid.Text = referenceWkt;
                }
                else
                {
                    this.textBox_epsg.Text = null;
                }
                foreach (Dictionary<String, Object> data in dataList)
                {
                    DataRow row = dataTable.NewRow();
                    foreach (String s in data.Keys)
                    {
                        row[s] = data[s];
                    }
                    dataTable.Rows.Add(row);
                }
                
                this.dataGridView_shp.DataSource = dataTable;
            };
            openFileDialog.ShowDialog();
        }

        private void DataGridView_shp_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            if (this.dataGridView_shp.Rows[e.RowIndex].HeaderCell.Value == null)
            {
                this.dataGridView_shp.Rows[e.RowIndex].HeaderCell.Value = e.RowIndex.ToString();
            }
            if (e.IsLastVisibleRow) this.dataGridView_shp.RowPostPaint -= DataGridView_shp_RowPostPaint;
        }

        private void DataGridViewFields_RowPostPaint(object sender, DataGridViewRowPostPaintEventArgs e)
        {
            if (this.dataGridViewFields.Rows[e.RowIndex].HeaderCell.Value == null)
            {
                this.dataGridViewFields.Rows[e.RowIndex].HeaderCell.Value = e.RowIndex.ToString();
            }
            if (e.IsLastVisibleRow)
            {
                this.dataGridViewFields.Rows[e.RowIndex].ReadOnly = true;
                dataGridViewFields.RowPostPaint -= DataGridViewFields_RowPostPaint;
            }
        }

        private void DataGridViewFields_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
            switch (e.Column.Name)
            {
                case "IsGeom":
                    e.Column.ReadOnly = true;
                    e.Column.Visible = false;
                    break;
                case "GdalIndex":
                    e.Column.ReadOnly = true;
                    e.Column.Visible = false;
                    break;
                case "GdalField":
                    e.Column.ReadOnly = true;
                    e.Column.HeaderText = "原属性名";
                    break;
                case "GdalFieldType":
                    e.Column.ReadOnly = true;
                    e.Column.HeaderText = "原类型";
                    break;
                case "Necessary":
                    e.Column.HeaderText = "是否必须";
                    break;
                case "SqlField":
                    e.Column.HeaderText = "入库属性";
                    break;
                case "SqlFieldType":
                    e.Column.HeaderText = "入库类型";
                    break;
                default:
                    break;
            }
            
        }

        private void tabControl1_Resize(object sender, EventArgs e)
        {
            this.dataGridViewFields.Height = this.tabControl1.Height - this.textBoxSrid.Height - this.textBoxSrid.Margin.Bottom - this.dataGridViewFields.Margin.Top - 32;
        }

        private void SetText(string str)
        {
            LogBox.AppendText(str);
            LogBox.AppendText(Environment.NewLine);
            LogBox.ScrollToCaret();
        }

        private void btn_file_path_Click(object sender, EventArgs e)
        {
            if(saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                textBox_geojson.Text = saveFileDialog1.FileName;
                if (geometries != null && geometries.Count > 0) btn_sava_file.Enabled = true;
            }
        }


        private void btn_sava_file_Click(object sender, EventArgs e)
        {
            if (properties == null || properties.Count == 0) return;

            if (textBox_geojson.Text == null || textBox_geojson.TextLength == 0)
            {
                MessageBox.Show("请输入正确的文件名和文件路径");
                return;
            }

            Driver dv = Ogr.GetDriverByName("GeoJSON");
            tabControl1.SelectTab("tab_log");
            if (dv == null)
            {
                Log.ErrorAsync("创建GeoJSON驱动失败");
                return ;
            }
            
            if (radioButton_export_all.Checked)
            {
                DataSource ds1 = Ogr.Open(shpFile, 0);
                if (null == ds1)
                {
                    Log.ErrorAsync($"打开文件失败！{shpFile}" );
                    return;
                }
                if (dv == null)
                {
                    Log.ErrorAsync("创建GeoJSON驱动失败");
                } else
                {
                    if (File.Exists(textBox_geojson.Text))
                    {
                        MessageBox.Show("请指定一个新文件名");
                        tabControl1.SelectTab("tab_export");
                        return;
                    }
                    dv.CopyDataSource(ds1, textBox_geojson.Text, new string[] { "ENCODING=UTF-8" });
                    dv.Dispose();
                    ds1.Dispose();
                    Log.InfoAsync($"导出 GeoJSON 文件 {textBox_geojson.Text} 成功！");
                }
                return;
            }
            DataSource ds = dv.CreateDataSource(textBox_geojson.Text, new string[] { "ENCODING=UTF-8" });
            Layer ly = ds.CreateLayer("json", this.spatialReference, this.wkbGeometryType, new string[]{ "ENCODING=UTF-8"});
            FeatureDefn fdefn = new FeatureDefn("f");
            foreach (FieldModel fm in fieldModels)
            {
                if (fm.Necessary && !fm.IsGeom)
                    //fdefn.AddFieldDefn(new FieldDefn(Encoding.UTF8.GetString(Encoding.Default.GetBytes(fm.SqlField)), fm.GdalFieldType));
                    fdefn.AddFieldDefn(new FieldDefn(fm.SqlField, fm.GdalFieldType));
            }
            fdefn.AddGeomFieldDefn(new GeomFieldDefn("geometry", this.wkbGeometryType));

            for (int i = 0; i < geometries.Count; i++)
            {
                Feature f = new Feature(fdefn);
                f.SetFID(i);
                foreach (FieldModel fm in fieldModels)
                {
                    if (fm.Necessary && !fm.IsGeom)
                    {
                        //if (gdalTools.Encoding == Encoding.UTF8)
                        //    f.SetField(fm.SqlField, properties[i][fm.GdalField].ToString());
                        //else
                            GdalTools.SetFieldValueString(f, fdefn.GetFieldIndex(fm.SqlField), properties[i][fm.GdalField].ToString());
                    }
                }
                f.SetGeometry(geometries[i]);
                ly.CreateFeature(f);
            }
            ly.SyncToDisk();
            ds.SyncToDisk();
            ds.Dispose();
            Log.InfoAsync($"导出 GeoJSON 文件 { textBox_geojson.Text} 成功！");
        }

        private void dataGridView1_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            geometries[e.RowIndex].ExportToWkt(out string wkt);
            textBox_wkt.Text = wkt;
            trackBar.Value = e.RowIndex;
            trackBar_ValueChanged(null, null);
        }

        /// <summary>
        /// 检测数据库是否存在指定表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_check_table_Click(object sender, EventArgs e)
        {
            if (textBox_table_name.Text == null || textBox_table_name.TextLength == 0) {
                label_exist.Text = "";
                dataGridView_table.DataSource = null;
                checkBox_clear.Checked = false;
                checkBox_clear.Visible = false;
                return; 
            }
            if(sqlTools.checkTableExist(textBox_table_name.Text))
            {
                label_exist.Text = "该表已经存在！请慎重导入并检查字段是否对应";
                label_exist.ForeColor = Color.Red;
                // 获取表字段信息
                dataGridView_table.DataSource = sqlTools.getTableInfo(textBox_table_name.Text).Tables[0];
                btn_import.Enabled = false;
                checkBox_clear.Visible = true;
                checkBox_clear.Checked = false;
            } else
            {
                label_exist.Text = "该表不存在可放心导入数据";
                label_exist.ForeColor = Color.Black;
                dataGridView_table.DataSource = null;
                btn_import.Enabled = true;
                checkBox_clear.Checked = false;
                checkBox_clear.Visible = false;
            }
        }
        /// <summary>
        /// 导入shp数据，先判断是否存在数据表，没有的话就创建
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btn_import_Click(object sender, EventArgs e)
        {
            if (!(fieldModels.Count > 0 && properties.Count > 0 && geometries.Count > 0))
            {
                MessageBox.Show("没有空间数据可导入！");
                return;
            }
            // 先判断是否需要创建表
            if (!sqlTools.checkTableExist(textBox_table_name.Text))
            {
                if (!sqlTools.CreateTable(textBox_table_name.Text, epsg, this.fieldModels))
                {
                    Log.ErrorAsync($"创建数据表 {textBox_table_name.Text} 失败");
                    MessageBox.Show($"创建数据表 {textBox_table_name.Text} 失败");
                    return;
                }
            }
            else if (checkBox_clear.Checked)
            {
                // 先清空表
                sqlTools.ClearTable(textBox_table_name.Text);
            }

            // 传递字段对应关系，属性数据集，空间数据集
            int result = sqlTools.Import(textBox_table_name.Text, fieldModels, properties, geometries);

            tabControl1.SelectTab("tab_log");
        }

        private void textBox_table_name_TextChanged(object sender, EventArgs e)
        {
            btn_import.Enabled = false;
            checkBox_clear.Visible = false;
            checkBox_clear.Checked = false;
            label_exist.Text = "请先检查表是否已经存在！";
        }
        /// <summary>
        /// 选择了先清空已有表
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkBox_clear_CheckedChanged(object sender, EventArgs e)
        {
            btn_import.Enabled = true;
        }

        private void trackBar_ValueChanged(object sender, EventArgs e)
        {
            if (geometries == null || geometries.Count == 0) return;
            int index = trackBar.Value;
            if (index >= properties.Count -1)
            {
                index = properties.Count - 1;
                trackBar.Maximum = index;
            }
            textBox_table_index.Text = $"{index + 1}";
            dataGridView_feature.Rows.Clear();
            foreach (var kv in properties[index])
            {
                dataGridView_feature.Rows.Add(new object[] {kv.Key, kv.Value.ToString()});
            }
            geometries[index].ExportToWkt(out string wkt);
            textBox_wkt.Text = wkt;
        }

        /// <summary>
        /// 只能输入0-9和退格键
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void TextBox_table_index_KeyPress(object sender, KeyPressEventArgs e)
        {
            if ((e.KeyChar < ((char)Keys.D0) || e.KeyChar > ((char)Keys.D9)) && e.KeyChar != ((char)Keys.Back) && e.KeyChar != ((char)Keys.Enter)) e.Handled = true;

            if (e.KeyChar == ((char)Keys.Enter))
            {
                int index = int.Parse(textBox_table_index.Text) - 1;
                if (index < 1 || textBox_total.TextLength < 1) index = 0;
                trackBar.Value = index;
            }
        }

        private void TextBox_table_index_TextChanged(object sender, EventArgs e)
        {
            if (textBox_total.TextLength < 1 || textBox_table_index.Text == "")
            {
                textBox_table_index.Text = "0";
                return;
            }
            int index = int.Parse(textBox_table_index.Text);
            if (textBox_total.Text != null && textBox_total.TextLength > 0 && index > int.Parse(textBox_total.Text))
            {
                textBox_table_index.Text = textBox_total.Text;
            }
        }

        private void splitContainer1_Panel1_SizeChanged(object sender, EventArgs e)
        {
            trackBar.Height = panel1.Height - textBox_total.Height - 6;
            dataGridView_feature.Height = trackBar.Height;
            dataGridView_feature.Width = panel1.Width - trackBar.Width - 6;
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (this.tabControl1.TabPages[tabControl1.SelectedIndex].Name == "tab_log")
            {
                this.LogBox.ScrollToCaret();
            }
        }

        private void About_ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            About a = new About();
            a.ShowDialog();
        }
    }
}
